運用共享技術有效地支持大量細粒度的物件。
享元模式是運用共享技術有效地支持大量細粒度的物件。他可以避免大量相似類的開銷,在軟體開發中如果需要生成大量細粒度的類別實體來表示數據,如果這些實體除了幾個參數外基本上都是相同的,這時候就可以使用享元模式來大幅度減少需要實體化類的數量。如果能把這些參數(指的這些類別實體不同的參數)移動類別實體外面,在方法調用時將他們傳遞進來,這樣就可以通過共享大幅度地減少單個實體的數目(這個也是享元模式的實現要領)。然而我們把類實體外面的參數稱為享元物件的外部狀態,把在享元物件內部定義稱為內部狀態。
不會隨著環境改變而產生變化,所以內部狀態是可以共享的部分。
會隨著環境改變而產生變化的部分,故這部分不可共享。
享元物件的外部狀態必須由Client保存。享元物件建立之後,在需要使用時將其傳入享元物件的內部。外部狀態之間是互相獨立的。
成員 | 功用 |
---|---|
Flyweight | 為介面或抽象類別的接口,其中定義ConcreteFlyweight的方法,非享元的外部狀態以參數的形式通過方法傳入。 |
ConcreteFlyweight | 實現Flyweight |
UnsharableFlyweight | 稱作複合享元物件,不可共享。但可分解成多個單純享元物件,則可共享。 |
FlyweightFactory | 負責建立及管理享元物件。當Client呼叫享元物件時,FlyweightFactory檢查是否存在符合要求的物件,存在則提供,不存在建立新的享元物件。 |
剛剛在介紹成員時有提到可以共享與不可以共享的部分,因為這樣,在實作中享元模式又分成單純享元模式以及複合享元模式。
所有實體享元物件都是可以共享的,不存在UnsharableFlyweight。
將單純享元物件使用組合形成複合享元物件,此物件不可共享。但若將其分解成單純享元物件,則可以。複合享元物件可對多個單純享元物件設定相同的外部狀態。
看完了一堆定義以及解釋,現在來實作看看。在一間公司內,在錄取之後都會有員工資訊,也會有名片。假設名片是個類別,分別需要CTO、CEO以及COO的名片。而每個名片的實體,也只有資訊不一樣而已,這時就可以套用享元模式。
首先先建立抽象享元,將方法先定義好。而show()傳入的參數則是外部狀態。
//抽象享元角色
interface NameCard {
// 外部狀態 msg
public void show(String msg);
}
而實體享元角色則實作抽象享元,在實體享元內的參數key則是可以共用的內部狀態。
//實體享元角色
class ConcreteNameCard implements NameCard {
// 內部狀態key
private String key;
ConcreteNameCard(String key) {
this.key=key;
System.out.println("Create " + key + "NameCard!!");
}
public void show(String msg) {
System.out.print("Hello!! I am " + key + ". ");
System.out.println(msg);
}
}
享元工廠內則管理各種享元元件。
//享元工廠角色
class NameCardFactory {
private static NameCardFactory FACTORY = new NameCardFactory();
private HashMap<String, NameCard> nameCards=new HashMap<String, NameCard>();
public static NameCardFactory getInstance() {
return FACTORY;
}
public NameCard getNameCard(String key) {
NameCard nameCard = (NameCard)nameCards.get(key);
if(nameCard != null) {
System.out.println("ConcreteNameCard " + key + " is exist!!");
}
else {
nameCard = new ConcreteNameCard(key);
nameCards.put(key, nameCard);
}
return nameCard;
}
}
最後Client經由享元工廠來使用享元元件。
public class FlyweightPattern {
public static void main(String[] args) {
NameCardFactory factory = new NameCardFactory();
NameCard ceo = factory.getNameCard("CEO");
ceo.show("This is my NameCard");
NameCard ceo2 = factory.getNameCard("CEO");
ceo2.show("This is my NameCard");
NameCard coo = factory.getNameCard("COO");
coo.show("Nice to meet you!!");
}
}
output
Create CEONameCard!!
Hello!! I am CEO. This is my NameCard
ConcreteNameCard CEO is exist!!
Hello!! I am CEO. This is my NameCard
Create COONameCard!!
Hello!! I am COO. Nice to meet you!!
複合享元模式的抽象享元以及實體享元和單純享元一樣。
//抽象享元角色
interface NameCard {
// 外部狀態 msg
public void show(String msg);
}
//實體享元角色
class ConcreteNameCard implements NameCard {
//內部狀態 key
private String key;
ConcreteNameCard(String key) {
this.key=key;
System.out.println("Create " + key + "NameCard!!");
}
public void show(String msg) {
System.out.print("Hello!! I am " + key + ". ");
System.out.println(msg);
}
}
複合享元多了一個複合享元角色,他將會由多個單純享元物件組成,不可共享。
//複合實體享元角色
class CompositeConcreteNameCard implements NameCard {
private Map<String, NameCard> nameCards = new HashMap<>();
@Override
public void show(String msg) {
for (Map.Entry<String, NameCard> entry : nameCards.entrySet()) {
entry.getValue().show(msg);
}
}
public void add(String key, NameCard nameCard) {
nameCards.put(key, nameCard);
}
public void remove(String type) {
nameCards.remove(type);
}
}
享元工廠內也新增了複合享元getNameCard()的方法,傳入參數從String改成List。
//享元工廠角色
class NameCardFactory {
private static NameCardFactory FACTORY = new NameCardFactory();
private HashMap<String, NameCard> nameCards=new HashMap<String, NameCard>();
public static NameCardFactory getInstance() {
return FACTORY;
}
// 單純享元模式
public NameCard getNameCard(String key) {
NameCard nameCard = (NameCard)nameCards.get(key);
if(nameCard != null) {
System.out.println("ConcreteNameCard " + key + " is exist!!");
}
else {
nameCard = new ConcreteNameCard(key);
nameCards.put(key, nameCard);
}
return nameCard;
}
// 複合享元模式
public NameCard getNameCard(List<String> keyList) {
CompositeConcreteNameCard nameCard = new CompositeConcreteNameCard();
for (String key : keyList) {
nameCard.add(key, this.getNameCard(key));
}
return nameCard;
}
}
Client端先將要建立的名片放到一個List內,然後將這List放到工廠內新建立的getNameCard()內,則可取得名片。而複合享元物件不可以共用,在最後可以看到我們建立一個先的複合享元物件,他並不等於第一個。
public class FlyweightPattern {
public static void main(String[] args) {
NameCardFactory factory = new NameCardFactory();
List<String> keyList = new ArrayList<>();
keyList.add("CTO");
keyList.add("COO");
keyList.add("CEO");
NameCard member = factory.getNameCard(keyList);
member.show("This is my NameCard");
NameCard member2 = factory.getNameCard(keyList);
// 不相等,複合享元模式的物件不可共享
System.out.println(member == member2);
}
}
output
Create CTONameCard!!
Create COONameCard!!
Create CEONameCard!!
Hello!! I am COO. This is my NameCard
Hello!! I am CEO. This is my NameCard
Hello!! I am CTO. This is my NameCard
ConcreteNameCard CTO is exist!!
ConcreteNameCard COO is exist!!
ConcreteNameCard CEO is exist!!
false
藉由共享來實現重用大量细粒度,減少需要建立的物件數量、避免大量類似物件的創建,進而提高資源使用率。
Flyweight:為介面或抽象類別的接口,其中定義ConcreteFlyweight的方法,非享元的外部狀態以參數的形式通過方法傳入。
ConcreteFlyweight:實體享元物件。
UnsharableFlyweight:複合享元物件,不可共享。可分解成多個單純享元物件,則可共享。
FlyweightFactory:負責建立及管理享元物件。當Client呼叫享元物件時,FlyweightFactory檢查是否存在符合要求的物件,存在則提供,不存在建立新的享元物件。
優點
1. 相同的物件只需要保存一份,降低了系統中的物件數量,也跟著降低系統細顆粒度物件對記憶體的壓力。
2. 享元模式的物件狀態相對是獨立的,不會影響到內部狀態,讓享元物件可在不同的環境被共享,。
缺點
1. 享元模式需要分離內部及外部狀態,這讓系統邏輯變得複雜化。
2. 為了是可以共享,享元模式需要將享元物件的部分狀態外部化,而讀取外部狀態使得執行時間拉長。
1. 一個系統中有大量的物件,這些物件耗費大量的記憶體。
2. 大部分物件可照內部狀態分組,且可將不同部分外部化,讓每一組只需保存一個內部狀態。
23种设计模式之享元(Flyweight)模式
享元模式(详解版)
设计模式21---设计模式之享元模式(Flyweight)(结构型)